# 错误处理
# try...except...finally...的错误处理机制
import logging


def Foo(i):
    try:
        print('try...')
        r = 10 / int(i)
        print('result:', r)
    except ZeroDivisionError as e:
        print('except ZeroDivisionError:', e)
    except ValueError as e:
        print('except ValueError:', e)
    else:   # 如果没有错误发生，可以在except语句块后面加一个else，当没有错误发生时，会自动执行else语句：
        print('no error...')
    finally:
        print('finally...')
    print('end...')


Foo(0)
print('-------------------------')
Foo(2)
print('-------------------------')
Foo('a')

# Python所有的错误都是从BaseException类派生的

# 使用try...except捕获错误还有一个巨大的好处，就是可以跨越多层调用，比如函数main()调用foo()，foo()调用bar()，结果bar()出错了，这时，只要main()捕获到了，就可以处理
print('-------------------------')


def main():
    try:
        first('0')
    except Exception as e:
        print('error:', e)
    finally:
        print('finally...')
    print('end...')


def first(a):
    return second(a)


def second(a):
    return 10 / int(a)
main()

print('-------------------------')

# 调用栈
# 如果错误没有被捕获，它就会一直往上抛，最后被Python解释器捕获，打印一个错误信息，然后程序退出


# def main():
#     first('0')
#
#
# def first(a):
#     return second(a)
#
#
# def second(a):
#     return 10 / int(a)
# main()


# 记录错误
# 如果不捕获错误，自然可以让Python解释器来打印出错误堆栈，但程序也被结束了。既然我们能捕获错误，就可以把错误堆栈打印出来，然后分析错误原因，同时，让程序继续执行下去
# Python内置的logging模块可以非常容易地记录错误信息


def main2():
    try:
        first2('0')
    except Exception as e:
        # print('error:', e)
        logging.exception(e)
    finally:
        print('finally...')
    print('end...')


def first2(a):
    return second2(a)


def second2(a):
    return 10 / int(a)
main2()
print('这里也执行了。。。')


# 抛出错误
# 如果要抛出错误，首先根据需要，可以定义一个错误的class，选择好继承关系，然后，用raise语句抛出一个错误的实例
class FooError(ValueError):
    pass


def foo(s):
    n = int(s)
    if n == 0:
        raise FooError('invalid value %s' % s)
    return 10 / n

print(foo(0))

#raise语句如果不带参数，就会把当前错误原样抛出。此外，在except中raise一个Error，还可以把一种类型的错误转化成另一种类型